{
"cells": [
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Callisto tket framework\n",
"\n",
"The tket framework is a quantum software framework primarily used for the development and execution of quantum algorithms (quantum circuits). It is designed to be platform-agnostic software, and there are a lot of extensions available which allow tket to interface with backends from a range of providers ( see tket extension ).\n",
"\n",
"Due to its popularity and the broad amount of available backends using this framework, we wrote an extension to tket that enables a user to communicate with the C12 emulator Callisto . The backend name is `CallistoBackend`.\n",
"\n",
"We need to create a circuit using the `Circuit` class to run a quantum algorithm."
]
},
{
"cell_type": "code",
"execution_count": 1,
"metadata": {
"collapsed": false,
"ExecuteTime": {
"end_time": "2024-10-08T14:54:52.019991Z",
"start_time": "2024-10-08T14:54:51.906497Z"
}
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"[X q[0];, CX q[0], q[1];]\n"
]
}
],
"source": [
"from pytket import Circuit\n",
"\n",
"circuit = Circuit(2)\n",
"circuit.X(0)\n",
"circuit.CX(0, 1)\n",
"\n",
"print(circuit.get_commands())"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Internally all circuits in the pytket framework are represented as directed acyclic graphs (DAG). Using ptyket util package it is possible to see the corresponding DAG of the chosen circuit."
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"A classical model for quantum computing is a model where the main program is run on the host computer, which occasionally sends off jobs to the quantum computer and retrieves the results. A backend represents a connection to some instance, either quantum hardware or an emulator. It presents a uniform interface for submitting circuits to be processed and retrieving the results. The main goal of this approach is to promote the development of platform-independent software, helping the code that the developers write to be more future-proof.\n",
"\n",
"Using tket's abstract class, `Backend,` it is possible to set different characteristics of a device that it should represent. The main properties that a backend need to implement are:\n",
"\n",
"1. The restrictions that are specific to specific quantum devices. Different constraints are encoded using the predicates, which are essentially a boolean property of a circuit that must return True to run the circuit on the target machine.\n",
"\n",
"2. Sending a circuit to the target device and examining the results. This process can be accomplished by calling `Backend.process_circuit()` or `Backend.process_circuits()` methods. These methods will send a circuit for execution and return an instance of `ResultHandle` as a result of their execution. The `ResultHandle` is a unique identifier that can be used to retrieve the actual results once the job has finished.\n",
"\n",
"3. Retrieval of the results. Obtaining the results of a successfully run job\n",
" is done using the `Backend.get_result()` method, which returns an instance of `BackendResult` class. The class `BackendResult` has methods that can be useful for obtaining different pieces of information about the job that has been run. These methods are:\n",
"`get_state()` - get the state vector\n",
"`get_shots()` - get the shots. The shots are returned as a 2D array of the result for each shot.\n",
"`get_counts()` - get the counts (it is obtained from the shots directly)\n",
"`get_density_matrix()` - get the density matrix of the job.\n",
"\n",
"\n",
"To implement the C12 emulator Callisto, we have developed `CallistoBackend` class with all the above options incorporated."
]
},
{
"cell_type": "code",
"execution_count": 2,
"outputs": [],
"source": [
"import os\n",
"os.environ[\"C12_TOKEN\"] = \"08071c09-4131-4369-ae5d-76be96a8cd86\"\n",
"os.environ[\"C12_HOST_URL\"]= \"api.dev-simulator.c12qe.net\""
],
"metadata": {
"collapsed": false,
"ExecuteTime": {
"end_time": "2024-10-08T14:55:11.492912Z",
"start_time": "2024-10-08T14:55:11.490014Z"
}
}
},
{
"cell_type": "code",
"execution_count": 3,
"metadata": {
"collapsed": false,
"ExecuteTime": {
"end_time": "2024-10-08T14:55:17.168060Z",
"start_time": "2024-10-08T14:55:15.127487Z"
}
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"('0b022428-9ae3-4190-9129-3f4743905208',)\n"
]
}
],
"source": [
"import os\n",
"from c12_callisto_clients.pytket.extensions.callisto import CallistoBackend\n",
"\n",
"access_token = os.getenv(\"C12_TOKEN\") # Token that is obtained for allowing the access to the system\n",
"backend_name = \"c12sim-iswap\"\n",
"\n",
"# create callisto backend instance\n",
"callisto = CallistoBackend(backend_name, token=access_token, verbose=False)\n",
"\n",
"# Get the ResultHandle instance\n",
"handle = callisto.process_circuit(circuit, n_shots=1024)\n",
"print(handle)"
]
},
{
"cell_type": "code",
"execution_count": 4,
"metadata": {
"collapsed": false,
"ExecuteTime": {
"end_time": "2024-10-08T14:56:11.741719Z",
"start_time": "2024-10-08T14:55:19.552613Z"
}
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"CircuitStatus(status=, message='', error_detail=None, completed_time=None, queued_time=None, submitted_time=None, running_time=None, cancelled_time=None, error_time=None, queue_position=None)\n",
"[[1 1]\n",
" [1 1]\n",
" [1 1]\n",
" ...\n",
" [1 1]\n",
" [1 1]\n",
" [1 1]]\n",
"Counter({(np.uint8(1), np.uint8(1)): np.int64(1024)})\n"
]
}
],
"source": [
"job_uuid = handle[0]\n",
"\n",
"# Get BackendResult instance\n",
"job_result = callisto.get_result(handle)\n",
"\n",
"\n",
"print(callisto.circuit_status(handle)) # Get the status of the circuit\n",
"print(job_result.get_shots())\n",
"print(job_result.get_counts())"
]
},
{
"cell_type": "code",
"execution_count": 5,
"metadata": {
"collapsed": false,
"ExecuteTime": {
"end_time": "2024-10-08T14:57:48.915299Z",
"start_time": "2024-10-08T14:57:48.615372Z"
}
},
"outputs": [
{
"ename": "CircuitNotValidError",
"evalue": "Circuit with index 0 in submitted does not satisfy MaxNQubitsPredicate(13) (try compiling with backend.get_compiled_circuits first).",
"output_type": "error",
"traceback": [
"\u001B[0;31m---------------------------------------------------------------------------\u001B[0m",
"\u001B[0;31mCircuitNotValidError\u001B[0m Traceback (most recent call last)",
"Cell \u001B[0;32mIn[5], line 6\u001B[0m\n\u001B[1;32m 3\u001B[0m circuit2\u001B[38;5;241m.\u001B[39mX(n)\n\u001B[1;32m 4\u001B[0m circuit2\u001B[38;5;241m.\u001B[39mCX(\u001B[38;5;241m0\u001B[39m, n)\n\u001B[0;32m----> 6\u001B[0m handle2 \u001B[38;5;241m=\u001B[39m \u001B[43mcallisto\u001B[49m\u001B[38;5;241;43m.\u001B[39;49m\u001B[43mprocess_circuit\u001B[49m\u001B[43m(\u001B[49m\u001B[43mcircuit2\u001B[49m\u001B[43m,\u001B[49m\u001B[43m \u001B[49m\u001B[43mn_shots\u001B[49m\u001B[38;5;241;43m=\u001B[39;49m\u001B[38;5;241;43m1024\u001B[39;49m\u001B[43m)\u001B[49m\n\u001B[1;32m 8\u001B[0m \u001B[38;5;66;03m# This should fail due to MaxNQubitsPredicate which controls the number of qubits that can be run on the emulator\u001B[39;00m\n",
"File \u001B[0;32m~/Desktop/Projects/simulator/C12QEsimulator/src/c12_callisto_clients/pytket/extensions/callisto/backends/callisto.py:321\u001B[0m, in \u001B[0;36mCallistoBackend.process_circuit\u001B[0;34m(self, circuit, n_shots, valid_check, **kwargs)\u001B[0m\n\u001B[1;32m 317\u001B[0m \u001B[38;5;250m\u001B[39m\u001B[38;5;124;03m\"\"\"\u001B[39;00m\n\u001B[1;32m 318\u001B[0m \u001B[38;5;124;03mRun a single circuit.\u001B[39;00m\n\u001B[1;32m 319\u001B[0m \u001B[38;5;124;03m\"\"\"\u001B[39;00m\n\u001B[1;32m 320\u001B[0m \u001B[38;5;28;01mif\u001B[39;00m valid_check:\n\u001B[0;32m--> 321\u001B[0m \u001B[38;5;28;43mself\u001B[39;49m\u001B[38;5;241;43m.\u001B[39;49m\u001B[43m_check_all_circuits\u001B[49m\u001B[43m(\u001B[49m\u001B[43m[\u001B[49m\u001B[43mcircuit\u001B[49m\u001B[43m]\u001B[49m\u001B[43m)\u001B[49m\n\u001B[1;32m 323\u001B[0m n_shots \u001B[38;5;241m=\u001B[39m \u001B[38;5;241m1024\u001B[39m \u001B[38;5;28;01mif\u001B[39;00m n_shots \u001B[38;5;129;01mis\u001B[39;00m \u001B[38;5;28;01mNone\u001B[39;00m \u001B[38;5;28;01melse\u001B[39;00m n_shots\n\u001B[1;32m 324\u001B[0m result_type \u001B[38;5;241m=\u001B[39m \u001B[38;5;124m\"\u001B[39m\u001B[38;5;124mcounts,statevector,density_matrix\u001B[39m\u001B[38;5;124m\"\u001B[39m\n",
"File \u001B[0;32m/opt/anaconda3/envs/c12_callisto_clients/lib/python3.10/site-packages/pytket/backends/backend.py:126\u001B[0m, in \u001B[0;36mBackend._check_all_circuits\u001B[0;34m(self, circuits, nomeasure_warn)\u001B[0m\n\u001B[1;32m 120\u001B[0m errors \u001B[38;5;241m=\u001B[39m (\n\u001B[1;32m 121\u001B[0m CircuitNotValidError(i, \u001B[38;5;28mrepr\u001B[39m(pred))\n\u001B[1;32m 122\u001B[0m \u001B[38;5;28;01mfor\u001B[39;00m pred \u001B[38;5;129;01min\u001B[39;00m \u001B[38;5;28mself\u001B[39m\u001B[38;5;241m.\u001B[39mrequired_predicates\n\u001B[1;32m 123\u001B[0m \u001B[38;5;28;01mif\u001B[39;00m \u001B[38;5;129;01mnot\u001B[39;00m pred\u001B[38;5;241m.\u001B[39mverify(circ)\n\u001B[1;32m 124\u001B[0m )\n\u001B[1;32m 125\u001B[0m \u001B[38;5;28;01mfor\u001B[39;00m error \u001B[38;5;129;01min\u001B[39;00m errors:\n\u001B[0;32m--> 126\u001B[0m \u001B[38;5;28;01mraise\u001B[39;00m error\n\u001B[1;32m 127\u001B[0m \u001B[38;5;28;01mif\u001B[39;00m nomeasure_warn:\n\u001B[1;32m 128\u001B[0m \u001B[38;5;28;01mif\u001B[39;00m circ\u001B[38;5;241m.\u001B[39mn_gates_of_type(OpType\u001B[38;5;241m.\u001B[39mMeasure) \u001B[38;5;241m<\u001B[39m \u001B[38;5;241m1\u001B[39m:\n",
"\u001B[0;31mCircuitNotValidError\u001B[0m: Circuit with index 0 in submitted does not satisfy MaxNQubitsPredicate(13) (try compiling with backend.get_compiled_circuits first)."
]
}
],
"source": [
"circuit2 = Circuit(20)\n",
"for n in range(1, 20):\n",
" circuit2.X(n)\n",
" circuit2.CX(0, n)\n",
"\n",
"handle2 = callisto.process_circuit(circuit2, n_shots=1024)\n",
"\n",
"# This should fail due to MaxNQubitsPredicate which controls the number of qubits that can be run on the emulator"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"collapsed": false
},
"outputs": [],
"source": []
}
],
"metadata": {
"kernelspec": {
"display_name": "Python 3 (ipykernel)",
"language": "python",
"name": "python3"
},
"language_info": {
"codemirror_mode": {
"name": "ipython",
"version": 3
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.9.13"
}
},
"nbformat": 4,
"nbformat_minor": 4
}